
#include <cstdio>
#include <BFont>
#include <BStyle>
#include <BWidget>
#include "member_BTabWidget.h"
#include "member_BAction.h"
#include "member_BWidget.h"

using namespace BWE;

member_BTabWidget::member_BTabWidget(BTabWidget* tabWidget)
{
	boss = tabWidget;
	tabAlign = Align_None;
	tabPart = Part_Top;
	tabVisible = true;
	tabSpacing = 3;
	arrowAlign = Align_Center;
	arrowVisible = true;
	autoTop = true;
	autoLeft = true;
	autoRight = true;
	autoBottom = true;

	dirty = true;
	hoverIndex = -1;
	pressed = false;

	arrow = new BButton(Graph_Arrow_Down, boss);
	arrow->setName("arrow");
	arrow->setCheckable(true);
	arrow->setVisible(false);

	board = new ITabBoard(boss);
	board->setName("board");
	board->setVerticalScrollHint(ScrollHint_None);
	board->setHorizontalScrollHint(ScrollHint_None);
	board->arrow = arrow;

	connect(board, Signal_Actived, &member_BTabWidget::slotBoardActived);
	connect(arrow, Signal_Checked, &member_BTabWidget::slotArrowChecked);
}
member_BTabWidget::~member_BTabWidget()
{

}

void member_BTabWidget::freshTabItems()
{
	const BStyle* style = boss->realStyle();
	const BFont* font = boss->realFont();

	BSize realTabSize = font->size();

	int totalWidth = 0;
	int totalHeight = 0;
	bool closable = false;
	if (items.size())
	{
		for (auto& item : items)
		{
			if (item.closeButton)
			{
				BSize hsize = item.closeButton->sizeHint();
				if (item.closeButton->setSize(hsize))
					item.dirty = true;
				closable = true;
			}
			if (item.dirtyStyle)
			{
				item.dispenseStyles(style);
				item.applyStyle(style);
				item.dirtyStyle = false;
				item.dirty = true;
			}
			if (item.dirty)
			{
				item.freshSize(font, 0);
				item.dirty = false;
			}
			if (realTabSize.height() < item.hintSize.height())
				realTabSize.height() = item.hintSize.height();
			if (realTabSize.width() < item.hintSize.width())
				realTabSize.width() = item.hintSize.width();
			totalWidth += item.hintSize.width() + tabSpacing;
			totalHeight += item.hintSize.height() + tabSpacing;
			item.visible = tabVisible;
		}
		totalWidth -= tabSpacing;
		totalHeight -= tabSpacing;
	}

	board->items.clear();
	board->dirty = true;
	board->fresh();

	BSize realArrowSize = arrow->adaptSize(arrow->sizeHint());
	if (realArrowSize.width() < arrowSize.width() && arrowSize.width() > 0)
		realArrowSize.width() = arrowSize.width();
	if (realArrowSize.height() < arrowSize.height() && arrowSize.height() > 0)
		realArrowSize.height() = arrowSize.height();

	if (autoTop || autoLeft || autoRight || autoBottom)
	{
		BPerch perch = boss->perch();
		if (autoTop)
			perch.top() = boss->preferSize(Part_Top).height();
		if (autoLeft)
			perch.left() = boss->preferSize(Part_Left).width();
		if (autoRight)
			perch.right() = boss->preferSize(Part_Right).width();
		if (autoBottom)
			perch.bottom() = boss->preferSize(Part_Bottom).height();
		if (tabPart == Part_Top || tabPart == Part_Bottom)
		{
			if (realTabSize.height() < realArrowSize.height())
				realTabSize.height() = realArrowSize.height();
			if (realTabSize.width() < tabSize.height())
				realTabSize.width() = tabSize.height();
			int tabHeight = realTabSize.height() + tabMargin.height();
			if (tabPart == Part_Top)
			{
				if (tabVisible && autoTop && perch.top() < tabHeight)
					perch.top() = tabHeight;
			}
			if (tabPart == Part_Bottom)
			{
				if (tabVisible && autoBottom && perch.bottom() < tabHeight)
					perch.bottom() = tabHeight;
			}
		}
		if (tabPart == Part_Left || tabPart == Part_Right)
		{
			if (realTabSize.width() < tabSize.width())
				realTabSize.width() = tabSize.width();
			int tabWidth = realTabSize.width() + tabMargin.width();
			if (tabPart == Part_Left)
			{
				if (tabVisible && autoLeft && perch.left() < tabWidth)
					perch.left() = tabWidth;
			}
			if (tabPart == Part_Right)
			{
				if (tabVisible && autoRight && perch.right() < tabWidth)
					perch.right() = tabWidth;
			}
		}
		boss->setPerch(perch);
	}

	if (tabPart == Part_Top || tabPart == Part_Bottom)
	{
		if (tabPart == Part_Top)
			tabArea = boss->rect(Part_Top);
		if (tabPart == Part_Bottom)
			tabArea = boss->rect(Part_Bottom);
		tabArea.top() += tabMargin.top();
		tabArea.left() += tabMargin.left();
		tabArea.right() -= tabMargin.right();
		tabArea.bottom() -= tabMargin.bottom();

		BRect tabRect = tabArea.align(tabAlign, bMin(totalWidth, tabArea.width()), realTabSize.height());
		arrow->setVisible(arrowVisible && totalWidth > tabArea.width());
		if (arrow->visible())
		{
			BRect arrowArea = tabArea;
			Align arrowPart = Align_Right;
			if (tabAlign & Align_HCenter)
				arrowPart = arrowAlign & Align_Left ? Align_Left : Align_Right;
			else if (tabAlign & Align_Right)
				arrowPart = Align_Left;
			if (arrowPart == Align_Left)
			{
				arrowArea.right() = arrowArea.left() + realArrowSize.width();
				if (tabRect.left() < arrowArea.right() + tabSpacing)
					tabRect.left() = arrowArea.right() + tabSpacing;
			}
			if (arrowPart == Align_Right)
			{
				arrowArea.left() = arrowArea.right() - realArrowSize.width();
				if (tabRect.right() > arrowArea.left() - tabSpacing)
					tabRect.right() = arrowArea.left() - tabSpacing;
			}
			arrow->setRect(arrowArea.align(arrowAlign, realArrowSize));
		}

		int x = tabRect.left();
		int y = tabRect.top();
		for (auto& item : items)
		{
			item.setPos(x, y);
			item.setSize(item.hintSize.width(), realTabSize.height());
			if (arrow->visible() && item.rect().right() > tabRect.right())
			{
				item.visible = false;
				item.dirtyStyle = true;
				board->items.append(&item);
			}
			x += item.size.width() + tabSpacing;
		}
	}
	if (tabPart == Part_Left || tabPart == Part_Right)
	{
		if (tabPart == Part_Left)
			tabArea = boss->rect(Part_Left);
		if (tabPart == Part_Right)
			tabArea = boss->rect(Part_Right);
		tabArea.top() += tabMargin.top();
		tabArea.left() += tabMargin.left();
		tabArea.right() -= tabMargin.right();
		tabArea.bottom() -= tabMargin.bottom();

		BRect tabRect = tabArea.align(tabAlign, realTabSize.width(), bMin(totalHeight, tabArea.height()));
		arrow->setVisible(arrowVisible && totalHeight > tabArea.height());
		if (arrow->visible())
		{
			BRect arrowArea = tabArea;
			Align arrowPart = Align_Bottom;
			if (tabAlign & Align_VCenter)
				arrowPart = arrowAlign & Align_Top ? Align_Top : Align_Bottom;
			else if (tabAlign & Align_Bottom)
				arrowPart = Align_Top;
			if (arrowPart == Align_Top)
			{
				arrowArea.bottom() = arrowArea.top() + realArrowSize.height();
				if (tabRect.top() < arrowArea.bottom() + tabSpacing)
					tabRect.top() = arrowArea.bottom() + tabSpacing;
			}
			if (arrowPart == Align_Bottom)
			{
				arrowArea.top() = arrowArea.bottom() - realArrowSize.height();
				if (tabRect.bottom() > arrowArea.top() - tabSpacing)
					tabRect.bottom() = arrowArea.top() - tabSpacing;
			}
			arrow->setRect(arrowArea.align(arrowAlign, realArrowSize));
		}

		int x = tabRect.left();
		int y = tabRect.top();
		for (auto& item : items)
		{
			item.setPos(x, y);
			item.setSize(realTabSize.width(), item.hintSize.height());
			if (arrow->visible() && item.rect().bottom() > tabRect.bottom())
			{
				item.visible = false;
				item.dirtyStyle = true;
				board->items.append(&item);
			}
			y += item.size.height() + tabSpacing;
		}
	}
	for (auto& item : items)
	{
		if (!item.closeButton)
			continue;
		if (item.visible)
		{
			item.closeArea = item.rectCenter;
			item.closeArea.left() = (int)item.rectCenter.right() - item.closeButton->width();
			item.closeArea += item.pos;
			BRect closeRect = item.closeArea.align(Align_Center, item.closeButton->size());
			item.closeButton->setRect(closeRect);
			item.closeButton->setParent(boss);
			item.closeButton->setFloating(true);
		}
		else
		{
			item.closeButton->setParent(board);
			item.closeButton->setFloating(false);
		}
	}
}

int member_BTabWidget::indexOf(const BWidget* widget)
{
	for (BList<TabItem>::iterator it = items.begin(); it != items.end(); it++)
	{
		TabItem& item = *it;
		if (item.widget == widget)
		{
			return it.index();
		}
	}
	return -1;
}
BRect member_BTabWidget::rectOf(const BWidget* widget)
{
	for (BList<TabItem>::iterator it = items.begin(); it != items.end(); it++)
	{
		TabItem& item = *it;
		if (item.widget == widget)
		{
			return it->rect();
		}
	}
	return BRect();
}

void member_BTabWidget::slotWidgetShow(BWidget* widget, const BValue& value)
{
	if (stack.last() != widget)
	{
		boss->setCurrent(widget);
	}
}
void member_BTabWidget::slotWidgetHide(BWidget* widget, const BValue& value)
{
	if (stack.last() == widget)
	{
		boss->emit(Signal_CurrentChanged);
	}
}
void member_BTabWidget::slotWidgetIconChanged(BObject* object, const BValue& value)
{
	auto it = items.find(object);
	if (it.valid())
	{
		it->dirty = true;
		it->icon = it->widget->icon();
		dirty = true;
		boss->fresh();
	}
}
void member_BTabWidget::slotWidgetTitleChanged(BObject* object, const BValue& value)
{
	auto it = items.find(object);
	if (it.valid())
	{
		it->dirty = true;
		it->text = it->widget->title();
		dirty = true;
		boss->fresh();
	}
}

void member_BTabWidget::slotCloseButtonStyled()
{
	dirty = true;
	board->dirty = true;
	board->fresh();
	boss->fresh();
}
void member_BTabWidget::slotCloseButtonClicked(BObject* object, const BValue& value)
{
	for (TabItem& item : items)
	{
		if (item.closeButton == object)
		{
			BValue result = item.widget;
			boss->emit(Signal_TabClosing, result);
			if (result == item.widget)
			{
				tabClosing = item.widget;
			}
			break;
		}
	}
}
void member_BTabWidget::slotBoardActived(BObject* object, const BValue& value)
{
	if (board->actived() == false)
	{
		arrow->setChecked(false);
	}
}
void member_BTabWidget::slotArrowChecked(BObject* object, const BValue& value)
{
	bool checked = value;
	if (checked)
	{
		BRect absRect = arrow->absoluteRect();
		BRect rootRect = board->root()->size();
		BPoint offset = board->popupOffset;
		int maxWidth = boss->width();

		BSize size = board->sizeHint();
		if (size.width() > maxWidth)
			size.width() = maxWidth;

		int x = tabArea.right() - size.width();
		int spaceUp = absRect.top();
		int spaceDown = rootRect.bottom() - absRect.bottom();
		if (spaceDown < size.height() && spaceDown < spaceUp)
		{
			size.height() = bMin(size.height(), spaceUp);
			board->setSize(size);
			board->setPos(offset.x() + x, offset.y() - board->height());
		}
		else
		{
			size.height() = bMin(size.height(), spaceDown);
			board->setSize(size);
			board->setPos(offset.x() + x, offset.y() + tabArea.bottom());
		}
		board->popup();
	}
	else
	{
		board->hide();
	}
}
